#include "client.h"


int password_callback(char *buf,int size,int rwflag,void * userdata){
    printf("Call back function called\n");
    strcpy(buf,"hlf0626"); 	
 	return strlen(buf);
} 
int verify_callbackf(int n,X509_STORE_CTX * x){
	  printf("^_^ Call verify_callbackf function called %d\n",n);
	  return n;
}


void 
SSL_init(){
    SSL_library_init();
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    ERR_load_SSL_strings();
    OpenSSL_add_all_algorithms();
}

int SSL_CTX_set_params(){
    SSL_METHOD *   meth = TLSv1_client_method(); 
    ctx = SSL_CTX_new (meth);                       
    CHK_NULL(ctx);
 
	int (*callback)(char *,int ,int ,void *)=&password_callback;
	SSL_CTX_set_default_passwd_cb(ctx,callback);  
 
    //SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);   
    int (*verify_callback)(int,X509_STORE_CTX *)=&verify_callbackf;
    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,verify_callback);
  
  
  
    SSL_CTX_load_verify_locations(ctx,CACERT,NULL);  
    /* */
    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(-2);
    }
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(-3);
    }
 
    if (!SSL_CTX_check_private_key(ctx)) {
        printf("Private key does not match the certificate public key\n");
        exit(-4);
    }

}
int 
tcp_connect(){
    int sd;  
    int err;
    struct sockaddr_in sa;
  
    #if defined(__MINGW32__)
     WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
        printf("WSAStartup()fail:%d\n",GetLastError());
        return -1;
    }
    #endif
  
    printf("Begin tcp socket...\n");
 
    sd = socket (AF_INET, SOCK_STREAM, 0);       
    CHK_ERR(sd, "socket");
 
    memset (&sa, '\0', sizeof(sa));
    sa.sin_family      = AF_INET;
    sa.sin_addr.s_addr = inet_addr (SERVER_ADDR);   /* Server IP */
    sa.sin_port        = htons     (PORT);          /* Server Port number */
 
    err = connect(sd, (struct sockaddr*) &sa,sizeof(sa)); 
    if(err<0){
        printf("Socket connect Err.");
        return -1;
    }
    return sd;
}

SSL* 
SSL_set_sd(int sd){
    printf("Begin SSL negotiation \n");
	SSL*ssl = SSL_new (ctx);                          
    SSL_set_fd (ssl, sd);
    int err = SSL_connect (ssl);
    if(err<=0){
        printf("ssl connect Err.");
        return NULL;
    }
    printf("SSL_connect finished\n");
    if(ssl)
        printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); 
    return ssl;
}

int verify_certificate(SSL * ssl){
    if(SSL_get_verify_result(ssl)!=X509_V_OK){
   	   printf("cli cert invalid.\n");
     	 return -1;
    }else{
      printf("cli cert is valid.\n");
    }

    char*    str;
    X509* server_cert = SSL_get_peer_certificate (ssl);
    if (server_cert != NULL) {
        printf ("Server certificate:\n");
 
        str = X509_NAME_oneline (X509_get_subject_name (server_cert),0,0);
        CHK_NULL(str);
        printf ("\t subject: %s\n", str);
        free (str);
 
        str = X509_NAME_oneline (X509_get_issuer_name  (server_cert),0,0);
        CHK_NULL(str);
        printf ("\t issuer: %s\n", str);
        free (str);
 
        X509_NAME_get_text_by_NID(X509_get_subject_name(server_cert),NID_commonName, str, 256);
   
        printf ("\t common name: %s\n", str);
        X509_free (server_cert);
    }
    else {
        printf ("Client does not have certificate.\n");
        return -1;
    }
   
    return 0;    
}
void 
ssl_write(void *arg){
    SSL *ssl=(SSL *)(arg);
    char     buf [1024];
    while(1){
        puts("client#");
        fgets(buf,1023,stdin);
        int err = SSL_write (ssl, buf, sizeof(buf) - 1); 
        if(strcmp(buf,"quit")==0) 
    	    break;
    }
}
void 
ssl_read_write(SSL *ssl,int sd){
  
    printf("Begin SSL data exchange\n");
    pthread_t  ntid;
 // int err = pthread_create(&ntid,NULL,ssl_write,ssl);

    while(1){
        char     buf [4096];
        int err;
        fgets(buf,1023,stdin);
        err = SSL_write (ssl, buf, sizeof(buf) - 1); 
        // err = SSL_write (ssl, "Hello World!", strlen("Hello World!")); 
        //CHK_SSL(err);
        err = SSL_read (ssl, buf, sizeof(buf) - 1); 
        //CHK_SSL(err);
        buf[err] = '\0';
        if(strcmp(buf,"quit")==0) 
    	    break;
        printf ("server: %s\n", buf); 
    }
    SSL_shutdown (ssl);  /* send SSL/TLS close_notify */
}
static void sigpipe_handle(int x){
	 printf("#######signal %d",x);
}


int 
main ()
{	
    //signal(SIGTERM, sigpipe_handle); // kill
	//signal(SIGHUP, sigpipe_handle); // kill -HUP  /  xterm closed
	//signal(SIGINT, sigpipe_handle); // Interrupt from keyboard
	//signal(SIGQUIT, sigpipe_handle); // Quit from keyboard
	signal(SIGPIPE, sigpipe_handle); // Some window managers cause this
    SSL_init();
    SSL_CTX_set_params();
    int sd;
    if((sd=tcp_connect())==-1){
  	    printf("tcp listen Err.\n");
      	return -1;
    }
    SSL*ssl = SSL_set_sd(sd);
    if(verify_certificate(ssl)==-1){
  	    printf("SSL verify cert Err.\n");
  	    return -1;
    }
    ssl_read_write(ssl,sd);
    shutdown (sd,2);
    SSL_free (ssl);
    SSL_CTX_free (ctx);
    return 0;
}
